using System;
using System.Collections.Generic;
using EnvDTE;
using System.IO;
using System.Diagnostics;

namespace Pretorianie.Tytan.Core.BaseGenerators
{
    /// <summary>
    /// Base code generator with site implementation for multiple files associated with one project item.
    /// </summary>
    public abstract class BaseMultiCodeGeneratorWithSite : BaseCodeGeneratorWithSite
    {
        /// <summary>
        /// Generates a set of file contents that later will be linked agains project item executing given current custom tool.
        /// </summary>
        protected abstract IList<GeneratedFileContent> GenerateContents(string inputFileContent);

        /// <summary>
        /// Generates the standard summary info that will be later placed in the file
        /// generated by Visual Studio itself.
        /// </summary>
        protected abstract string GenerateSummary(string inputFileContent);

        #region Multi-file Processing

        /// <summary>
        /// Gets the prefix for of file names, when saving data to the disk.
        /// </summary>
        protected virtual string UniqueFilePrefix
        {
            get
            {
                string[] externalParams = (string.IsNullOrEmpty(FileNamespace) ? null : FileNamespace.Split(';'));

                return (externalParams == null || externalParams.Length < 2 ? null : externalParams[1]);
            }
        }

        /// <summary>
        /// Encapsulates all the operations performed over the ProjectItem to add references
        /// to a set of dynamically generated files.
        /// </summary>
        private void ProcessMultiContentGeneration(string inputFileContent)
        {
            ProjectItem currentItem;
            IList<GeneratedFileContent> fileContents;
            List<string> addedItems = new List<string>();
            string folder;
            string outputFileName;
            string autoGeneratedFile;
            string uniqueFileName;
            FileStream outputFile = null;
            byte[] outputData;
            bool isReferenced;


            // get active project item:
            currentItem = GetProjectItem();
            folder = Path.GetDirectoryName(InputFilePath);

            // generate new files contents:
            fileContents = GenerateContents(inputFileContent);

            // create files:
            if (fileContents != null)
            {
                foreach (GeneratedFileContent c in fileContents)
                {
                    // generate file name that will not collide with others:
                    outputFileName = GetUniqueName(currentItem.ProjectItems, folder, UniqueFilePrefix, c.FileName, false, out isReferenced, out uniqueFileName);
                    try
                    {
                        // create the file:
                        outputFile = File.Create(outputFileName);

                        // and write down the data:
                        outputData = c.BinaryData;
                        outputFile.Write(outputData, 0, outputData.Length);
                        outputFile.Flush();

                        // store reference:
                        if (!isReferenced)
                            c.SetNewItemProperties(currentItem.ProjectItems.AddFromFile(outputFileName));
                        addedItems.Add(uniqueFileName);
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine(ex.Message);
                    }
                    finally
                    {
                        if (outputFile != null)
                            outputFile.Close();
                        outputFile = null;
                    }
                }

                // remove old no-more-valid elements:
                autoGeneratedFile = Path.GetFileNameWithoutExtension(currentItem.Name) + CodeDefaultExtension;
                foreach (ProjectItem i in currentItem.ProjectItems)
                {
                    if (i.Name != autoGeneratedFile && !addedItems.Contains(i.Name))
                        i.Delete();
                }
            }
        }

        /// <summary>
        /// Generate unique file name, based on given prefix, overwrite condition and check if this file belongs to references.
        /// </summary>
        private static string GetUniqueName(ProjectItems items, string folder, string namePrefix, string fileName, bool canOverWrite, out bool isAlreadyReferenced, out string uniqueFileName)
        {
            string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
            string fileNameExtension = Path.GetExtension(fileName);
            string result;
            int counter = 0;

            do
            {
                result = counter == 0
                             ? Path.Combine(folder, uniqueFileName = (namePrefix + fileName))
                             : Path.Combine(folder,
                                            uniqueFileName =
                                            string.Format("{0}{1}({2}){3}", namePrefix, fileNameWithoutExtension,
                                                          counter, fileNameExtension));
                counter++;

                // check if given file name should be left in the project references:
                isAlreadyReferenced = false;
                foreach (ProjectItem i in items)
                {
                    if (i.Name == uniqueFileName)
                    {
                        isAlreadyReferenced = true;
                        break;
                    }
                }

                //Trace.WriteLine(string.Format("FileName: {0}, isRef: {1}", result, isAlreadyReferenced));

            } while ((File.Exists(result) && !canOverWrite) && !isAlreadyReferenced);

            return result;
        }

        #endregion

        #region Wrappers for VisualStudio standard behaviour

        /// <summary>
        /// This is a default extension for item created by Visual Studio.
        /// Inside that item only some public summary will be written.
        /// </summary>
        protected override string CodeDefaultExtension
        {
            get { return ".summary"; }
        }

        /// <summary>
        /// Extend standard one-output generation with multiple instances.
        /// </summary>
        protected override string GenerateStringCode(string inputFileContent)
        {
            ProcessMultiContentGeneration(inputFileContent);
            return GenerateSummary(inputFileContent);
        }

        #endregion
    }
}
